﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace LexicalAnalysisCs
{
    public class CodeDecorating
    {
        private LexicalAnalyzerCs mLacs;
        private StringBuilder mCode;

        public string MCode
        {
            get { return mCode.ToString(); }
        }

        public CodeDecorating(string inFileName, string outFileName)
        {
            this.mLacs = new LexicalAnalyzerCs(inFileName, outFileName);
            FileStream fs = new FileStream(@inFileName, FileMode.Open);
            StreamReader sr = new StreamReader(fs);
            mCode = new StringBuilder(sr.ReadToEnd());
            sr.Close();
            fs.Close();
        }

        // in c++, only one of the declaration and definition needs to be added inline(or both)
        // then the function is inlined.
        // the result is restored in m_Code;
        public void InsertInlines()
        {
            List<LexicalAnalyzerCs.StrInt> wordList = mLacs.getResList;

            // modified here.
            System.Collections.Hashtable baseType = new Hashtable();
            System.Collections.Hashtable templateType = new Hashtable();

            string cppBaseType = "void,int,double,float,string,char";
            string cppTemplateType = "vector,map,list,queue,dequeue,stack";

            string[] sp = cppBaseType.Split(',');
            foreach (var s in sp)
            {
                baseType.Add(s, 1);
            }
            string[] tp = cppTemplateType.Split(',');
            foreach (var s in tp)
            {
                templateType.Add(s, 1);
            }

            for (int i = 0; i < wordList.Count; i++)
            {
                if (wordList[i].mContent == "class" || wordList[i].mContent == "struct")
                {
                    baseType.Add(wordList[i + 1].mContent, 1);
                    i++;
                }
                if (wordList[i].mContent == "typedef")
                {
                    if (wordList[i + 1].mContent == "struct")
                    {
                        i += 4;
                        int bkt = 1;
                        for (; i < wordList.Count; i++)
                        {
                            if (wordList[i].mContent == "{")
                            {
                                bkt++;
                            } else if (wordList[i].mContent == "}")
                            {
                                bkt--;
                                if (bkt == 0)
                                {
                                    baseType.Add(wordList[i + 1].mContent, 1);
                                    i++;
                                    break;
                                }
                            }
                        }
                    } else
                    {
                        baseType.Add(wordList[i + 2].mContent, 1);
                        i += 2;
                    }
                }
            }

            // for testing
//             foreach (DictionaryEntry de in baseType)
//             {
//                 Console.Write(de.Key);
//                 Console.Write(' ');
//                 Console.WriteLine(de.Value);
//             }
//             Console.ReadLine();
//             foreach (DictionaryEntry de in templateType)
//             {
//                 Console.Write(de.Key);
//                 Console.Write(' ');
//                 Console.WriteLine(de.Value);
//             }

            int brackets = 0; // brackets == 0 means the code is global;
            List<int> insertPos = new List<int>();
            for (int i = 0; i < wordList.Count; i++)
            {
                if (wordList[i].mContent == "{" || wordList[i].mContent == "(")
                {
                    brackets++;
                } else if (wordList[i].mContent == "}" || wordList[i].mContent == ")")
                {
                    brackets--;
                }

                if (brackets == 0)
                {
                    if (baseType.ContainsKey(wordList[i].mContent) && wordList[i + 2].mContent == "(" && wordList[i - 1].mContent != "inline")
                    {
//                        mCode.Insert(wordList[i].mEndIndex - wordList[i].mContent.Length, "inline ");
                        if (wordList[i + 1].mContent != "main")
                        {
                            insertPos.Add(wordList[i].mEndIndex - wordList[i].mContent.Length);
                        }
                    }
                }

                if (brackets == 0 && templateType.ContainsKey(wordList[i].mContent))
                {
                    for (int j = i + 1; j < wordList.Count; j++)
                    {
                        if (wordList[j].mContent == ">" && wordList[j + 2].mContent == "(" && wordList[i - 1].mContent != "inline")
                        {
                            insertPos.Add(wordList[i].mEndIndex - wordList[i].mContent.Length);
                            i = j;
                            break;
                        }
                    }
                }
            }

            // Finally we insert the inlines.
            for (int i = insertPos.Count - 1; i >= 0; i--)
            {
                mCode.Insert(insertPos[i], "inline ");
            }

//             FileStream fs = new FileStream("out2.txt", FileMode.OpenOrCreate);
//             StreamWriter sw = new StreamWriter(fs);
//             sw.WriteLine(mCode);
//             sw.Close();
//             fs.Close();
        }

        public bool OutputCodesToFile(string inFileName)
        {
            try
            {
                FileStream fs = new FileStream(inFileName, FileMode.Open);
                StreamWriter sw = new StreamWriter(fs);
                sw.WriteLine(mCode);
                sw.Close();
                fs.Close();
                return true;
            } catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
        }
    }
}
